<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="http://localhost:6688/puzzle/">
    <title>拼图游戏</title>
    <style>
      * {
        box-sizing: border-box;
      }
      div.container div:hover {
        box-shadow: 0 0 10px black;
      }
      #choose_container {
        width: 500px;
        margin: 0 auto;
      }
      input {
        display: block;
        margin: 10px auto;
      }

      #imageFile {
        width: 180px;
      }
      #level {
        width: 80px;
      }
      #level_container {
        width: 200px;
        margin: 0 auto;
      }
    </style>
  </head>
  <body>
  <a href="./images/img2.jpg">aaa</a>
    <div id="choose_container">
      <input type="file" id="imageFile" accept="image/*" />
      <div id="level_container">
        <label for="level">选择难度:</label>
        <select id="level">
          <option selected>3</option>
          <option>4</option>
          <option>5</option>
          <option>6</option>
          <option>7</option>
          <option>8</option>
          <option>9</option>
        </select>
      </div>
    </div>

    <script type="module">
      import { Util } from "./script/util.js";
      import { PuzzleArray } from "./script/puzzle.js";

      class PuzzleDOM {
        constructor(level, imageURL, imgWidth, imgHeight) {
          this.level = level;
          this.imageURL = imageURL;
          this.puzzleArray = new PuzzleArray(level);
          this._initDomElement(imgWidth, imgHeight);
          this._initDomEvent();
        }
        _initDomElement(imgWidth, imgHeight) {
          let level = this.level;
          this.container = Util.createElement("div", {
            width: imgWidth,
            height: imgHeight,
            backgroundColor: "green",
            margin: "40px auto",
            display: "flex",
            flexWrap: "wrap",
          });
          let container = this.container;
          container.className = "container";

          let width = (1 / level) * parseInt(container.style.width);
          let height = (1 / level) * parseInt(container.style.height);
          for (let i = 0; i < level; i++) {
            let positionY = -i * height + "px";
            for (let j = 0; j < level; j++) {
              let positionX = -j * width + "px";
              let div = Util.createElement("div", {
                width: width + "px",
                height: height + "px",
                border: "1px solid black",
                backgroundImage: `url(${this.imageURL})`,
                backgroundPosition: `${positionX} ${positionY}`,
              });
              let targetX = (this.puzzleArray.t2fArr[i][j][0] - i) * height;
              let targetY = (this.puzzleArray.t2fArr[i][j][1] - j) * width;
              div.style.transform = `translate(${targetY}px, ${targetX}px)`;
              div.setAttribute("rX", i);
              div.setAttribute("rY", j);
              container.appendChild(div);
            }
          }
          let choose_container = document.getElementById("choose_container");
          document.body.insertBefore(container, choose_container);
        }
        _initDomEvent() {
          let element = this.container.firstElementChild;
          while (element) {
            element.addEventListener("mousedown", _mousedownHandler);
            element.addEventListener("mousemove", _mousemoveHandler);
            element.addEventListener("mouseup", _mouseupHandler);
            element = element.nextElementSibling;
          }
        }

        destory() {
          // 清除事件处理器
          let element = this.container.firstElementChild;
          while (element) {
            document.removeEventListener("mousedown", _mousedownHandler);
            document.removeEventListener("mousemove", _mousemoveHandler);
            document.removeEventListener("mouseup", _mouseupHandler);

            element = element.nextElementSibling;
          }
          document.body.removeChild(this.container);
        }
      }
      let level = 3;
      let imgURL = "./images/image.jpg";
      let imgWidth = "500px";
      let imgHeight = "500px";
      let puzzle = new PuzzleDOM(level, imgURL, imgWidth, imgHeight);

      document.getElementById("level").addEventListener("change", (event) => {
        level = parseInt(event.target.value);
        puzzle.destory();
        puzzle = new PuzzleDOM(level, imgURL, imgWidth, imgHeight);
      });

      document
        .getElementById("imageFile")
        .addEventListener("change", (event) => {
          if (event.target.files.length > 0) {
            let file = event.target.files[0];
            imgURL = window.URL.createObjectURL(file);
            let img = new Image();
            img.src = imgURL;
            img.onload = function () {
              imgWidth = this.width + "px";
              imgHeight = this.height + "px";
              puzzle.destory();
              puzzle = new PuzzleDOM(level, imgURL, imgWidth, imgHeight);
            };
          }
        });

      function _mousedownHandler(event) {
        let target = event.target;
        target.style.opacity = "0.5";
        target.isMousedown = true;
        target.style.zIndex = "2";
        target.pX = event.pageX;
        target.pY = event.pageY;

        [target.translateX, target.translateY] = _getTranslate(target);

        function _getTranslate(element) {
          let transform = target.style.transform;
          let beginIndex = transform.indexOf("(") + 1;
          let endIndex = transform.lastIndexOf(")");
          transform = transform.substring(beginIndex, endIndex);
          let arr = transform.split(", ");
          return [parseInt(arr[0]), parseFloat(arr[1])];
        }
      }
      function _mousemoveHandler(event) {
        let target = event.target;
        if (!target.isMousedown) {
          return;
        }
        let offsetX = event.pageX - target.pX + target.translateX;
        let offsetY = event.pageY - target.pY + target.translateY;
        target.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
      }
      function _mouseupHandler(event) {
        let target = event.target;
        target.style.opacity = "";
        target.isMousedown = false;
        target.style.zIndex = "";

        let element = getSwapElement(event, target);
        if (element === null || element === target || !swap(element, target)) {
          target.style.transform = `translate(${target.translateX}px, ${target.translateY}px)`;
        } else {
          if (puzzle.puzzleArray.isSuccess()) {
            setTimeout(() => {
              if (confirm("拼图成功,是否重新开始")) {
                puzzle.destory();
                puzzle = new PuzzleDOM(level, imgURL, imgWidth, imgHeight);
              }
            }, 550);
          }
        }

        function swap(e1, e2) {
          // 标签上的编号
          let [e1Rx, e1Ry] = [
            getIntAttribute(e1, "rX"),
            getIntAttribute(e1, "rY"),
          ];
          let [e2Rx, e2Ry] = [
            getIntAttribute(e2, "rX"),
            getIntAttribute(e2, "rY"),
          ];
          // 目前所在的位置
          let [e1Fx, e1Fy] = puzzle.puzzleArray.t2fArr[e1Rx][e1Ry];
          let [e2Fx, e2Fy] = puzzle.puzzleArray.t2fArr[e2Rx][e2Ry];

          let [a, b] = [Math.abs(e2Fx - e1Fx), Math.abs(e2Fy - e1Fy)];
          // 不是相邻元素,不能换位置
          if (a + b != 1) {
            return false;
          }

          let width = parseInt(e1.style.width);
          let height = parseInt(e1.style.height);
          let e1OffsetX = e2Fx - e1Rx;
          let e1OffsetY = e2Fy - e1Ry;

          let e2OffsetX = e1Fx - e2Rx;
          let e2OffsetY = e1Fy - e2Ry;

          e1.style.transition = "0.5s";
          e2.style.transition = "0.5s";
          e1.style.transform = `translate(${e1OffsetY * width}px, ${
            e1OffsetX * height
          }px)`;
          e2.style.transform = `translate(${e2OffsetY * width}px, ${
            e2OffsetX * height
          }px)`;
          puzzle.puzzleArray.swap(e1Rx, e1Ry, e2Rx, e2Ry);
          setTimeout(() => {
            e1.style.transition = "";
            e2.style.transition = "";
          }, 400);
          return true;
        }
        //获取实际上要交换的元素，没有则返回空
        function getSwapElement(event, target) {
          let container = document.querySelector("div.container");
          let element = container.firstElementChild;
          while (element) {
            let oT = element.offsetTop;
            let oL = element.offsetLeft;
            let oH = element.offsetHeight;
            let oW = element.offsetWidth;
            // 获取拖动元素的中心店坐标
            let x =
              event.pageX - event.offsetX + parseInt(target.style.width) / 2;
            let y =
              event.pageY - event.offsetY + parseInt(target.style.height) / 2;
            //中心点位于其他元素中，可以交换
            if (x > oL && x < oL + oW && y > oT && y < oT + oH) {
              return getRealElement(element);
            }
            element = element.nextElementSibling;
          }
          return null;
        }
        function getRealElement(element) {
          let [rx, ry] = [
            getIntAttribute(element, "rX"),
            getIntAttribute(element, "rY"),
          ];
          let [x, y] = puzzle.puzzleArray.f2tArr[rx][ry];
          return document.querySelector(`[rX="${x}"][rY="${y}"]`);
        }

        function getIntAttribute(element, name) {
          return parseInt(element.getAttribute(name));
        }
      }
    </script>
  </body>
</html>
